#include "mfg.h"
#include "mfg_Stack.h"
#include "mfg_coord.h"
#include "cmd/cmd_cfg.h"
#include "cmd/cmd_nwk.h"
#include "cmd/cmd_zcl.h"

#include "mfg_zcl.h"
#include "zcl_ha.h"
#include "osal_nv.h"
/* macro */
#ifndef MFG_EP_LIST_SIZE
#define MFG_EP_LIST_SIZE      16
#endif

#define  NODE_JOIN_WAIT_TIME   5   //sec
#define  NODE_ZDP_WAIT_TIME    10    //sec
#define  NODE_ZDP_RETRY        3

/* typedef */
typedef struct
{
  void*  next;
  uint16 dest;
  uint8 *pSeqNum;
  uint8 cmdId;
  uint8 cmdSize;
  uint8 cmdData[];
}mfgZdpQueue_t;

typedef struct
{
  void *next;
  uint8 mac[8];
  uint8 timeSec;
}mfgWhiteList_t;

typedef struct
{
  void* next;
  uint8  mac[8];
  uint16 nodeId;
  uint16 parent;
  uint8  join;
  uint8  type;
  uint8  timeSec;
  uint8  retry;
  uint8  seq;
  uint8  epCnt;
  uint8  epIdx;
  uint8  epList[MFG_EP_LIST_SIZE];
  void (*cb)(void*);
}mfgNodeList_t;

/* variable */
mfgWhiteList_t *mfgWhiteListHead = NULL;
mfgNodeList_t *mfgNodeListHead = NULL;
mfgZdpQueue_t *mfgZdpQueueHead = NULL;

bool mfgWhiteListEnable = FALSE;

uint8  JoiningCount = 0;
bool   JoiningLock = FALSE;
//Node callback
void MFG_QueryActiveEpCB(void*);
//Device callback
void MFG_QuerySimpleDescCB(void*);
//Device Verify Join
//void MFG_VerifyJoinCB(void*);

/* static function */
static void mfgWlistRefresh(void);
static void mfgWlistPush(mfgWhiteList_t* item);
static mfgWhiteList_t* mfgWlistFind(uint8 *mac);

//Node List Function
mfgNodeList_t* mfgNodeCreatNew(uint8 *mac);

ZStatus_t MFG_ZclPluginCallback( zclIncoming_t *pInMsg );

void mfgNodeRefresh(void);
void mfgNodePush(mfgNodeList_t* item);
mfgNodeList_t* mfgNodeFindByMac(uint8* mac);
mfgNodeList_t* mfgNodeFindByNodeId(uint16 nodeId);

void* mfgLeaveIndCB( void *param ); // ZDO_LEAVE_IND_CBID
void* mfgPermitJoinCB( void *param ); // ZDO_PERMIT_JOIN_CBID
void* mfgTcJoinCB( void *param ); // ZDO_TC_DEVICE_CBID

void MFG_ExchangeKeyCB(bdb_TCLinkKeyExchProcess_t *pInd);

//local func
//timer

//extern function

const mfgServerCmd_t mfgServerCmdList[] =
{
 { ZCL_CLUSTER_ID_GEN_SCENES, 0x05 },
};

void MFG_InitCallback(uint8 taskId)
{
  pZDSecMgrDeviceValidateCallback = mfgDeviceValidateCallback;  //SecMgr DeviceValidate
  pZDSecMgrDeviceLeaveNotifyCallback = mfgDeviceLeaveNotifyCallback;  //SecMgr DeviceLeaveNotify
  zclGeneral_RegisterUnsupportCallback( MFG_ZclPluginCallback );
  zcl_registerPlugin( 0x0015, 0x0018, MFG_ZclPluginCallback );
  //0x0019 = OTA
  zcl_registerPlugin( 0x001A, 0x001F, MFG_ZclPluginCallback );
  //0x0020 = GP
  zcl_registerPlugin( 0x0021, 0x0FFF, MFG_ZclPluginCallback );
  //0x1000 = TL
  zcl_registerPlugin( 0x1001, 0xFBFF, MFG_ZclPluginCallback );
  zcl_registerPlugin( 0xFC00, 0xFCFF, MFG_ZclPluginCallback );
  
  bdb_RegisterTCLinkKeyExchangeProcessCB( MFG_ExchangeKeyCB );
  ZDO_RegisterForZdoCB( ZDO_LEAVE_IND_CBID, mfgLeaveIndCB );
  ZDO_RegisterForZdoCB( ZDO_PERMIT_JOIN_CBID, mfgPermitJoinCB );
  ZDO_RegisterForZdoCB( ZDO_TC_DEVICE_CBID, mfgTcJoinCB );
  
  ZDO_RegisterForZDOMsg ( taskId, NWK_addr_rsp );
  ZDO_RegisterForZDOMsg ( taskId, IEEE_addr_rsp );
  ZDO_RegisterForZDOMsg ( taskId, Simple_Desc_rsp );
  ZDO_RegisterForZDOMsg ( taskId, Active_EP_rsp );
  ZDO_RegisterForZDOMsg ( taskId, Bind_rsp );
  ZDO_RegisterForZDOMsg ( taskId, Unbind_rsp );
  ZDO_RegisterForZDOMsg ( taskId, Mgmt_Bind_rsp );
  ZDO_RegisterForZDOMsg ( taskId, Mgmt_Leave_rsp );
  ZDO_RegisterForZDOMsg ( taskId, Device_annce );
}

/*
* @fn          MFG_PeriodTick
*
* @brief       called in mfg task per second
*
* @param       none
*
* @return      none
*
* */
void MFG_PeriodCallback(void)
{
  mfgWlistRefresh();
  mfgNodeRefresh();
  cmdZdp_SeqTablePoll();
}

/*
* @fn          mfgDeviceValidateCallback
*
* @brief       point to pZDSecMgrDeviceValidateCallback in zd_sec_mgr.h
*
* @param       ZDSecMgrDevice_t in zd_sec_mgr.c
*
* @return      ZSuccess or ZNwkUnknownDevice
*
* */
ZStatus_t mfgDeviceValidateCallback( uint16 nwkAddr, uint8* extAddr, uint16 parentAddr )
{
  if( MFG_WhiteListEnable() )
  {
    //lookup white list
    if( MFG_WhiteListCheck(extAddr) )
    {
      return ZSuccess;
    }
    //out put denied extAddr
    cmdCfg_Denied( extAddr, parentAddr );
    return ZNwkUnknownDevice;
  }
  if( JoiningLock )
  {
    return ZNwkUnknownDevice;
  }
  return ZSuccess;
}

/*
* @fn          mfgDeviceLeaveNotifyCallback
*
* @brief       point to pZDSecMgrDeviceValidateCallback in zd_sec_mgr.h
*
* @param       ZDSecMgrDevice_t in zd_sec_mgr.c
*
* @return      ZSuccess or ZNwkUnknownDevice
*
* */
void mfgDeviceLeaveNotifyCallback( uint16 nwkAddr, uint8 *extAddr, uint16 parentAddr )
{
  cmdCfg_LeaveNotify( nwkAddr, extAddr, parentAddr );
}

/*
* @fn          MFG_WhiteListAdd
*
* @brief       add a ext-address into whitelist
*
* @param       mac - extAddress
*
* @return      message pointer
*
* */
bool MFG_WhiteListAdd( uint8 *mac )
{
  mfgWhiteList_t *newItem = MFG_malloc( sizeof(mfgWhiteList_t) );
  if(newItem)
  {
    newItem->next = NULL;
    newItem->timeSec = 240;
    memcpy(newItem->mac, mac, 8);
    mfgWlistPush(newItem);
    return true;
  }
  return false;
}

/*
* @fn          MFG_WhiteListCheck
*
* @brief       check white list by ext-address
*
* @param       mac - extAddress
*
* @return      True of False
*
* */
bool MFG_WhiteListCheck(uint8 *mac)
{
  mfgWhiteList_t* item = mfgWlistFind(mac);
  if(item)
  {
    item->timeSec = 0;
    return true;
  }
  return false;
}

/*
* @fn          MFG_WhiteListEnable
*
* @brief       
*
* @param       NONE
*
* @return      True of False
*
* */
bool MFG_WhiteListEnable(void)
{
  return mfgWhiteListEnable;
}

/*
* @fn          MFG_WhiteListSet
*
* @brief       
*
* @param       enable TRUE or FALSE
*
* @return      NONE
*
* */
void MFG_WhiteListSet( uint8 enable )
{
  mfgWhiteListEnable = enable;
}

/*
* @fn          MFG_LeaveInd
*
* @brief       message ZDO_LEAVE_IND_CBID
*
* @param       nodeId - short Address
*              mac - ext Address
*              parant - parent short address
* 
* @return      None
*
* */
void MFG_LeaveInd(NLME_LeaveInd_t *pInd)
{
  cmdCfg_LeaveInd( *pInd );
}

/*
* @fn          MFG_TcJoinInd
*
* @brief       message ZDO_TC_DEVICE_CBID
*
* @param       nodeId - short Address
*              mac - ext Address
*              parant - parent short address
* 
* @return      None
*
* */
void MFG_TcJoinInd( ZDO_TC_Device_t *pInd )
{
  if( pInd->secure )
  {
    cmdCfg_NodeJoinNotify( pInd->extAddr, pInd->nwkAddr, pInd->parentAddr, JOIN_MODE_REJOIN, 0xFF );
  }
  else
  {
    mfgNodeList_t* item = mfgNodeCreatNew(pInd->extAddr);
    if( item )
    {
      item->nodeId = pInd->nwkAddr;
      item->parent = pInd->parentAddr;
    }
  }
}

/*
* @fn          MFG_DeviceAnnounceInd
*
* @brief       message 
*
* @param       nodeId - short Address
*              mac - ext Address
*              parant - parent short address
* 
* @return      None
*
* */
void MFG_DeviceAnnounceInd( ZDO_DeviceAnnce_t ind )
{
  mfgNodeList_t* item = mfgNodeFindByMac( ind.extAddr );
  uint8 type = NODE_ENDDEV;
  if( ind.capabilities & CAPINFO_DEVICETYPE_FFD )
  {
    type = NODE_ROUTER;
  }
  uint16 parent = 0xFFFE;
  uint8 mode = JOIN_MODE_REJOIN;
  if(item)
  {
    parent = item->parent;
    item->type = type;
    item->nodeId = ind.nwkAddr;
    if( item->join )
    {
      mode = JOIN_MODE_JOIN;
      if( (item->cb == MFG_QueryActiveEpCB) && (item->retry == NODE_ZDP_RETRY) && (item->timeSec > 0) )
      {
        if( (ind.capabilities & CAPINFO_RCVR_ON_IDLE) == 0 )
        {
          item->timeSec = 0;
        }
      }
    }
    else
    {
      mode = JOIN_MODE_TC_REJOIN;
    }
  }
  cmdCfg_NodeJoinNotify( ind.extAddr, ind.nwkAddr, parent, mode, type );
}

/*
* @fn          MFG_VerifyLinkKey
*
* @brief       message zstackmsg_CmdIDs_BDB_TC_LINK_KEY_EXCHANGE_NOTIFICATION_IND
*
* @param       bdb_TCLinkKeyExchProcess_t
* 
* @return      None
*
* */
void MFG_VerifyLinkKey(bdb_TCLinkKeyExchProcess_t ind)
{
  //output aps-key
  cmdCfg_KeyVerify( ind.status, ind.extAddr );
  if(ind.status == 0)
  {
    JoiningCount ++;
    if( JoiningCount >= COORD_JOINING_LIMIT )
    {
      JoiningLock = TRUE;
    }

    mfgNodeList_t* item = mfgNodeCreatNew(ind.extAddr);
    if( item )
    {
      item->join = 1;
      item->cb = MFG_QueryActiveEpCB;
      item->retry = NODE_ZDP_RETRY;
    }
  }
  else
  {
    JoiningCount --;
    if( JoiningCount == 0 )
    {
      JoiningLock = FALSE;
    }

    mfgNodeList_t* item = mfgNodeFindByMac(ind.extAddr);
    if(item)
    {
      if( (item->cb == MFG_QueryActiveEpCB) && (item->retry == NODE_ZDP_RETRY) && (item->timeSec > 0) )
      {
        item->timeSec = 0; // read active EP at once.
      }
      if( (item->type == NODE_NONE) && (ind.status==BDB_TC_LK_EXCH_PROCESS_EXCH_FAIL) )
      {
        cmdCfg_NodeJoinNotify(item->mac, item->nodeId, item->parent, JOIN_MODE_ERRO, item->type);
      }
    }
  }
}

void MFG_ExchangeKeyCB(bdb_TCLinkKeyExchProcess_t *pInd)
{
  MFG_VerifyLinkKey(*pInd);
}

/*
* @fn          MFG_ActiveEpInd
*
* @brief       message zstackmsg_CmdIDs_ZDO_ACTIVE_EP_RSP
*
* @param       nodeId - short Address
*              seq - rsp trans seq
*              epCnt - endpoint count
*              epArray - endpoint list
* 
* @return      None
*
* */
bool MFG_ActiveEpInd(ZDO_ActiveEndpointRsp_t *pRsp)
{
  mfgNodeList_t* find = mfgNodeFindByNodeId(pRsp->nwkAddr);
  if(find->seq == pRsp->transSeq)
  {
    if(pRsp->status == ZSuccess)
    {
      find->epCnt = pRsp->cnt;
      if(find->epCnt > MFG_EP_LIST_SIZE)
      {
        find->epCnt = MFG_EP_LIST_SIZE;
      }
      find->epIdx = 0;
      memcpy( find->epList, pRsp->epList, find->epCnt );
      find->cb = MFG_QuerySimpleDescCB;
      find->retry = NODE_ZDP_RETRY;
      find->timeSec = 0;
    }
    else
    {
      find->cb = NULL;
      find->retry = 0;
      find->timeSec = 0;
    }
    return true;
  }
  return false;
}

/*
* @fn          MFG_SimpleDescInd
*
* @brief       message zstackmsg_CmdIDs_ZDO_SIMPLE_DESC_RSP
*
* @param       
* 
* @return      None
*
* */
bool MFG_SimpleDescInd( ZDO_SimpleDescRsp_t rsp )                        
{
  mfgNodeList_t* find = mfgNodeFindByNodeId(rsp.nwkAddr);
  if(find)
  {
    if(find->seq == rsp.transSeq)
    {
      if(rsp.status == ZSuccess)
      {
        if( find->epList[find->epIdx] == rsp.simpleDesc.EndPoint )
        {
          mfgDevSn_t sn;
          mfgDevAddr_t dev;
          bool end = FALSE;
          sn[0] = rsp.simpleDesc.EndPoint;
          memcpy( sn+1, find->mac, 8);
          dev[0] = LO_UINT16(find->nodeId);
          dev[1] = HI_UINT16(find->nodeId);
          dev[2] = rsp.simpleDesc.EndPoint;

          find->epIdx ++;
          if( find->epIdx < find->epCnt ) //query next endpoint
          {
            find->retry = NODE_ZDP_RETRY;
            find->timeSec = 0;
          }
          else //end query
          {
            find->cb = NULL;
            find->retry = 0;
            find->timeSec = 0;
            end = TRUE;
          }
          // output simpleDesc info
          cmdCfg_DevJoinNotify(end, sn, dev, rsp.simpleDesc.AppProfId, rsp.simpleDesc.AppDeviceId,
                                 rsp.simpleDesc.AppNumInClusters, rsp.simpleDesc.pAppInClusterList,
                                 rsp.simpleDesc.AppNumOutClusters, rsp.simpleDesc.pAppOutClusterList);
        }
        return true;
      }
      find->cb = NULL;
      find->retry = 0;
      find->timeSec = 0;
      return true;
    }
  }
  return false;
}

/*
* @fn          MFG_MatchSeverCmd
*
* @brief
*
* @param
*
* @return      TRUE - match success
*
* */
bool MFG_MatchSeverCmd(uint16 clusterId, uint8 cmdId)
{
    uint8 num = sizeof(mfgServerCmdList)/sizeof(mfgServerCmd_t);
    uint8 i;
    for( i=0; i< num; i++ )
    {
        if( ( mfgServerCmdList[i].clusterId == clusterId ) && ( mfgServerCmdList[i].cmdId == cmdId ) )
        {
            return TRUE;
        }
    }
    return FALSE;
}

//
typedef struct
{
  uint8  ieee[8];
  uint16 nodeId;
  uint8 ep;
}mfgQueryErro_t;

static void MFG_QueryErro(uint8 status, uint8 endpoint, uint8 transID, void* cnfParam)
{
  if(cnfParam)
  {
    mfgQueryErro_t *erro = (mfgQueryErro_t*)cnfParam;
    if(status != ZSuccess)
    {
      cmdCfg_DeviceErro(erro->ieee, erro->nodeId, erro->ep, status);
    }
    MFG_free(cnfParam);
  }
}

//Query Activ EP
void MFG_QueryActiveEpCB(void *p)
{
  mfgNodeList_t *item = p;
  mfgQueryErro_t *erro = MFG_malloc(sizeof(mfgQueryErro_t));
  if( erro == NULL )
  {
    cmdCfg_DeviceErro( item->mac, item->nodeId, 0xFF, ZMemError );
    return;
  }
  if( item->retry )
  {
    uint8 status;
    // set error buffer
    memcpy( erro->ieee, item->mac, 8 );
    erro->nodeId = item->nodeId;
    erro->ep = 0xFF;
    // Set confirm
    ZDP_SetSendConfirm( MFG_QueryErro, erro );
    item->seq = ZDP_SeqNum;
    // send Active EP Req
    zAddrType_t dstAddr;
    dstAddr.addrMode = Addr16Bit;
    dstAddr.addr.shortAddr = item->nodeId;
    status = ZDP_ActiveEPReq( &dstAddr, item->nodeId, 0 );
    if(ZSuccess != status)
    {
      cmdCfg_DeviceErro(erro->ieee, erro->nodeId, erro->ep, status);
      MFG_free(erro);
    }
    item->timeSec = NODE_ZDP_WAIT_TIME;
  }
  else
  {
    cmdCfg_DeviceErro( item->mac, item->nodeId, 0xFF, ZFailure );
  }
}

//Query Simple Desc
void MFG_QuerySimpleDescCB(void *p)
{
  mfgNodeList_t *item = p;
  mfgQueryErro_t *erro = MFG_malloc(sizeof(mfgQueryErro_t));
  if( erro == NULL)
  {
    cmdCfg_DeviceErro( item->mac, item->nodeId, item->epList[item->epIdx], ZMemError );
    return;
  }
  if( item->retry )
  {
    uint8 status;
    //set error buffer
    memcpy( erro->ieee, item->mac, 8 );
    erro->nodeId = item->nodeId;
    erro->ep = item->epList[item->epIdx];
    // Set confirm
    ZDP_SetSendConfirm( MFG_QueryErro, erro );
    item->seq = ZDP_SeqNum;
    // send Simple Desc Req
    zAddrType_t dstAddr;
    dstAddr.addrMode = Addr16Bit;
    dstAddr.addr.shortAddr = item->nodeId;
    uint8 endpoint = item->epList[item->epIdx];
    // send command
    status = ZDP_SimpleDescReq( &dstAddr, item->nodeId, endpoint, 0 );
    if(ZSuccess != status)
    {
      cmdCfg_DeviceErro(erro->ieee, erro->nodeId, erro->ep, status);
      MFG_free(erro);
    }
    item->timeSec = NODE_ZDP_WAIT_TIME;
  }
  else
  {
    cmdCfg_DeviceErro( item->mac, item->nodeId, item->epList[item->epIdx], ZFailure );
  }
}

/*
*        Local Function
*/

/*
* @fn          mfgWlistRefresh
*
* @brief       refresh whitelist
*
* @param       none
*
* @return      none
*
* */
void mfgWlistRefresh(void)
{
  mfgWhiteList_t* poll = mfgWhiteListHead;
  mfgWhiteList_t* pre = NULL;
  while(poll)
  {
    void *next = poll->next;
    if(poll->timeSec == 0)
    {
      mfgWhiteList_t* free = poll;
      if(free == mfgWhiteListHead)
      {
        mfgWhiteListHead = free->next;
      }
      else if(pre != NULL)
      {
        pre->next = free->next;
      }
      MFG_free(free);
    }
    else
    {
      poll->timeSec -= 1;
      pre = poll;
    }
    poll = next;
  }
}

/*
* @fn          mfgWlistPush
*
* @brief       add whitelist item into queue
*
* @param       none
*
* @return      none
*
* */
static void mfgWlistPush(mfgWhiteList_t* item)
{
  if(mfgWhiteListHead == NULL)
  {
    mfgWhiteListHead = item;
  }
  else
  {
    mfgWhiteList_t* tail = mfgWhiteListHead;
    while(tail->next != NULL)
    {
      tail = tail->next;
    }
    tail->next = item;
  }
}

/*
* @fn          mfgWlistFind
*
* @brief       lookup whitelist item by extAddr
*
* @param       mac - extAddr
*
* @return      whitelist item
*
* */
static mfgWhiteList_t* mfgWlistFind(uint8 *mac)
{
  mfgWhiteList_t* find = mfgWhiteListHead;
  while(find)
  {
    if(0 == memcmp(mac, find->mac, 8))
    {
      return find;
    }
    find = find->next;
  }
  return NULL;
}

/*
* @fn          mfgNodeCreatNew
*
* @brief       creat new item into NodeList
*
* @param       mac - input IEEE addr
*
* @return      item record
*
* */
mfgNodeList_t* mfgNodeCreatNew(uint8 *mac)
{
  mfgNodeList_t* item = mfgNodeFindByMac(mac);
  if( item == NULL )
  {
    item = MFG_malloc(sizeof(mfgNodeList_t));
    if( item )
    {
      memset(item, 0, sizeof(mfgNodeList_t));
      memcpy(item->mac, mac, 8);
      mfgNodePush(item);
    }
  }
  if(item)
  {
    item->timeSec = NODE_JOIN_WAIT_TIME;//refresh wait time
  }
  return item;
}

/*
* @fn          mfgNodeRefresh
*
* @brief       Node List Refresh
*
* @param       None
*
* @return      None
*
* */
void mfgNodeRefresh(void)
{
  mfgNodeList_t* poll = mfgNodeListHead;
  mfgNodeList_t* pre = NULL;
  while(poll)
  {
    void *next = poll->next;
    if((poll->timeSec == 0)&&(poll->retry == 0))
    {
      mfgNodeList_t* free = poll;
      if(free == mfgNodeListHead)
      {
        mfgNodeListHead = free->next;
      }
      else if(pre != NULL)
      {
        pre->next = free->next;
      }
      if(poll->cb)
      {
        poll->cb(poll);
      }
      MFG_free(free);
    }
    else
    {
      if(poll->timeSec > 0)
      {
        poll->timeSec -= 1;
      }
      else
      {
        if(poll->cb)
        {
          poll->cb(poll);
        }
        if(poll->retry)
        {
          poll->retry -= 1;
        }
      }
      pre = poll;
    }
    poll = next;
  }
}

/*
* @fn          mfgNodePush
*
* @brief       Add new item into Node List
*
* @param       item - new item of Node List
*
* @return      None
*
* */
void mfgNodePush(mfgNodeList_t* item)
{
  if(item)
  {
    if(mfgNodeListHead == NULL)
    {
      mfgNodeListHead = item;
    }
    else
    {
      mfgNodeList_t* tail = mfgNodeListHead;
      while(tail->next)
      {
        tail = tail->next;
      }
      tail->next = item;
    }
  }
}

/*
* @fn          mfgNodeFindByMac
*
* @brief       lookup Node List by ext-Address
*
* @param       mac - ext Address
*
* @return      Node List item
*
* */
mfgNodeList_t* mfgNodeFindByMac(uint8* mac)
{
  mfgNodeList_t* find = mfgNodeListHead;
  while(find)
  {
    if(0 == memcmp(find->mac,mac,8))
    {
      return find;
    }
    find = find->next;
  }
  return NULL;
}

/*
* @fn          mfgNodeFindByNodeId
*
* @brief       lookup Node List by short address
*
* @param       nodeId - short address
*
* @return      Node List item
*
* */
mfgNodeList_t* mfgNodeFindByNodeId(uint16 nodeId)
{
  mfgNodeList_t* find = mfgNodeListHead;
  while(find)
  {
    if(find->nodeId == nodeId)
    {
      return find;
    }
    find = find->next;
  }
  return NULL;
}

// ZDO CB
void* mfgLeaveIndCB( void *param )
{
  NLME_LeaveInd_t *pLeaveInd = (NLME_LeaveInd_t*)param;
  cmdCfg_LeaveInd( *pLeaveInd );
  return NULL;
}

void* mfgPermitJoinCB( void *param )
{
  uint8 duration = *( (uint8 *)param );
  cmdCfg_PermitJoinNotify( duration );
  return NULL;
}

void* mfgTcJoinCB( void *param )
{
  MFG_TcJoinInd( (ZDO_TC_Device_t *)param );
  return NULL;
}

void MFG_processZDOMgs(zdoIncomingMsg_t* pMsg)
{
  switch( pMsg->clusterID )
  {
  case NWK_addr_rsp:
    {
      ZDO_NwkIEEEAddrResp_t *pAddrRsp;
      pAddrRsp = ZDO_ParseAddrRsp( pMsg );
      cmdZdp_NwkAddrRsp( *pAddrRsp );
      osal_mem_free( pAddrRsp );
    }
    break;
    
  case IEEE_addr_rsp:
    {
      ZDO_NwkIEEEAddrResp_t *pAddrRsp;
      pAddrRsp = ZDO_ParseAddrRsp( pMsg );
      cmdZdp_IeeeAddrRsp( *pAddrRsp );
      osal_mem_free( pAddrRsp );
    }
    break;
    
  case Simple_Desc_rsp:
    {
      ZDO_SimpleDescRsp_t simpleRsp;
      ZDO_ParseSimpleDescRsp( pMsg, &simpleRsp );
      if( FALSE == MFG_SimpleDescInd(simpleRsp) )
      {
        cmdZdp_SimpleDescRsp(simpleRsp);
      }
      // free cluster list
      if( simpleRsp.simpleDesc.pAppInClusterList )
      {
        osal_mem_free( simpleRsp.simpleDesc.pAppInClusterList );
      }
      if( simpleRsp.simpleDesc.pAppOutClusterList )
      {
        osal_mem_free( simpleRsp.simpleDesc.pAppOutClusterList );
      }
    }
    break;
    
  case Active_EP_rsp:
    {
      ZDO_ActiveEndpointRsp_t *epRsp = ZDO_ParseEPListRsp( pMsg );
      if ( epRsp )
      {
        if(FALSE == MFG_ActiveEpInd(epRsp))
        {
          cmdZdp_ActiveEpRsp(epRsp);
        }
        osal_mem_free( epRsp );
      }
    }
    break;
    
  case Bind_rsp:
    {
      uint8 status = ZDO_ParseBindRsp( pMsg );
      cmdZdp_BindRsp(pMsg->srcAddr.addr.shortAddr, pMsg->TransSeq, status);
    }
    break;
    
  case Unbind_rsp:
    {
      uint8 status = ZDO_ParseBindRsp( pMsg );
      cmdZdp_UnbindRsp(pMsg->srcAddr.addr.shortAddr, pMsg->TransSeq, status);
    }
    break;
    
  case Mgmt_Bind_rsp:
    {
      ZDO_MgmtBindRsp_t *pBindRsp = ZDO_ParseMgmtBindRsp( pMsg );
      if ( pBindRsp )
      {
        cmdZdp_BindTableRsp( pMsg->srcAddr.addr.shortAddr, pBindRsp );
        osal_mem_free( pBindRsp );
      }
    }
    break;
    
  case Mgmt_Leave_rsp:
    {
      uint8 status = ZDO_ParseMgmtLeaveRsp( pMsg );
      cmdZdp_LeaveRsp( pMsg->srcAddr.addr.shortAddr, pMsg->TransSeq, status);
    }
    break;
    
  case Device_annce:
    {
      ZDO_DeviceAnnce_t devAnn;
      ZDO_ParseDeviceAnnce( pMsg, &devAnn );
      MFG_DeviceAnnounceInd(devAnn);
    }
    break;
  }
}

void MFG_RandomExtPanid(void)
{
  //uint8 defExtPanid[8] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF};
  
  osal_nv_read( ZCD_NV_EXTENDED_PAN_ID, 0, Z_EXTADDR_LEN, zgExtendedPANID );
  if( true == sAddrExtCmp( zgExtendedPANID, NLME_GetExtAddr() ) )
  {
    uint8  extAddr[8];
    uint16 random = osal_rand();
    sAddrExtCpy( extAddr, NLME_GetExtAddr() );
    //Create the key from the seed
    uint8 i;
    for(i = 0; i < 8; i++)
    {
      extAddr[i] ^= (random & 0x000000FF);
      random >>= 1;
      zgExtendedPANID[i] ^= extAddr[i];
    }
    osal_cpyExtAddr( _NIB.extendedPANID, zgExtendedPANID );
    osal_nv_write( ZCD_NV_EXTENDED_PAN_ID, 0, Z_EXTADDR_LEN, zgExtendedPANID );
  }
}

